from markupsafe import Markup
from datetime import datetime, timedelta
from collections import defaultdict

from odoo import api, fields, models, _, SUPERUSER_ID
from odoo.exceptions import UserError, ValidationError
from odoo.tools import float_compare, float_round, format_datetime, float_is_zero
import logging

_logger = logging.getLogger(__name__)


class MrpWorkorderExt(models.Model):
    _inherit = 'mrp.workorder'

    curr_user = fields.Many2one(string="Operator", comodel_name='res.users', default=lambda self: self.env.user, store=False)

    prev_step_done = fields.Boolean(string="Is the previous process completed", compute="_compute_prev_step_done", store=False)
    is_allow_move = fields.Boolean(string="Is counting allowed", compute="_compute_is_allow_move", store=False)
    done_line_ids = fields.One2many('mrp.workorder.line', 'workorder_id', 'Work Order Production Details')
    done_act_qty = fields.Float(string="Actual Processing Quantity", store=True, default=0, copy=False)

    qty_wip = fields.Float('Work In Process (WIP)', compute='compute_qty_floor', store=True, digits='Product Unit of Measure')
    qty_op_produced = fields.Float('Process Output', compute='compute_qty_floor', store=True, digits='Product Unit of Measure')
    qty_op_reject = fields.Float('Process Scrap', compute='compute_qty_floor', store=True, digits='Product Unit of Measure')

    qty_wip_pnl = fields.Float('Work In Process (PNL)', compute='compute_qty_floor', store=True, digits='Product Unit of Measure')
    qty_op_produced_pnl = fields.Float('Process Output (PNL)', compute='compute_qty_floor', store=True, digits='Product Unit of Measure')
    qty_op_reject_pnl = fields.Float('Process Scrap (PNL)', compute='compute_qty_floor', store=True, digits='Product Unit of Measure')

    qty_wip_set = fields.Float('Work In Process (SET)', compute='compute_qty_floor', store=True, digits='Product Unit of Measure')
    qty_op_produced_set = fields.Float('Process Output (SET)', compute='compute_qty_floor', store=True, digits='Product Unit of Measure')
    qty_op_reject_set = fields.Float('Process Scrap (SET)', compute='compute_qty_floor', store=True, digits='Product Unit of Measure')

    value_pnl = fields.Float(related='product_id.value_pnl', store=True)  # 废弃字段 不要使用
    value_set = fields.Float(related='product_id.value_set', store=True)  # 废弃字段 不要使用
    step = fields.Integer(string="Step", compute='_compute_step', store=True, readonly=False)

    is_subcontracting = fields.Boolean(string='Subcontracting', copy=False)
    sub_readonly = fields.Boolean(compute='_compute_sub_readonly', default=False)

    pol_id = fields.One2many('purchase.order.line', 'workorder_id', string='Purchase Order')
    pol_ids = fields.Many2many('purchase.order.line', string='Purchase Order')

    param_ids = fields.One2many('mrp.workorder.param.line', 'workorder_id', 'Process Parameters', copy=True)

    work_station_id = fields.Many2one('work.station', related="workcenter_id.s_work_station_id", string='Work Station')

    s_qty_pnl_production = fields.Integer('Qty(PNL)', compute='_compute_qty_panel_remaining', store=True)
    s_qty_panel_remaining = fields.Integer('PANEL Quantity', compute='_compute_qty_panel_remaining', store=True)
    s_scrap_ids = fields.One2many('s.workorder.scrap.record', 'workorder_id', string='Scrap Lines')
    s_return_scrap_ids = fields.One2many('s.workorder.return.scrap.record', 'workorder_id', string='Return Scrap Lines')

    s_stop_time = fields.Float(string='Duration of stay(hour)', copy=False, compute='_compute_stop_time')

    s_station_order = fields.Many2one('s.mrp.work.station.order', 'Station Order', copy=False)

    # 钻孔表
    s_drill_operation_ids = fields.Many2many('mrp.routing.workcenter.drill', string='Drill Operation')
    s_back_drill_operation_ids = fields.Many2many('mrp.routing.workcenter.back.drill', string='Drill Operation')

    def _compute_stop_time(self):
        for rec in self:
            previous = rec._get_previous_work()
            if not previous:
                rec.s_stop_time = 0
            elif previous and previous.state != 'done':
                rec.s_stop_time = 0
            else:
                date_finished = datetime.now() if rec.state != 'done' else rec.date_finished
                # 有可能完成状态没有完成时间
                date_finished = date_finished or datetime.now()
                date_start = previous.date_finished
                if not date_start:
                    rec.s_stop_time = 0
                else:
                    diff_time = date_finished - date_start
                    if isinstance(diff_time, int):
                        rec.s_stop_time = 0
                    else:
                        rec.s_stop_time = diff_time.total_seconds() / 3600

    @api.constrains('state')
    def constrains_state_one(self):
        for rec in self:
            if rec.state != 'done':
                continue
            rec.consumable_materials()

    @api.depends('write_date')
    def _compute_sub_readonly(self):
        for rec in self:
            if rec.sub_apply_line_ids:
                rec.sub_readonly = True
            else:
                rec.sub_readonly = False

    @api.depends('qty_production', 'qty_reported_from_previous_wo', 'qty_produced', 'production_id.product_uom_id')
    def _compute_qty_panel_remaining(self):
        for wo in self:
            wo.s_qty_pnl_production = wo.qty_production / wo.value_pnl if wo.value_pnl else 0
            if wo.production_id.product_uom_id:
                wo.s_qty_panel_remaining = max(float_round((wo.qty_production - wo.qty_reported_from_previous_wo - wo.qty_produced) / wo.value_pnl, precision_rounding=wo.production_id.product_uom_id.rounding), 0)
            else:
                wo.s_qty_panel_remaining = 0

    @api.depends('operation_id')
    def _compute_step(self):
        for i in self:
            i.step = i.operation_id.step

    def _check_first_work(self):
        """
        判断是否为首工序
        """
        self.ensure_one()
        if self.search([('production_id', '=', self.production_id.id), ('step', '<', self.step)], order="step desc", limit=1):
            return False
        else:
            return True

    def _get_previous_work(self):
        """
        获取上工序
        """
        self.ensure_one()
        return self.env['mrp.workorder'].search([
            ('production_id', '=', self.production_id.id),
            ('step', '<', self.step)
        ], order="step desc", limit=1)

    # @api.constrains('qty_op_produced', 'qty_op_produced_pnl', 'qty_op_produced_set', 'qty_op_reject', 'qty_op_reject_pnl', 'qty_op_reject_set')
    # def constrains_qty_op_produced(self):
    #     for rec in self:
    #         previous = rec._get_previous_work()
    #         if previous:
    #             previous_qty,  previous_pnl = previous.qty_op_produced, previous.qty_op_produced_pnl
    #         else:
    #             previous_qty, previous_pnl = rec.production_id.product_qty, rec.production_id.panel_qty
    #
    #         rounding = rec.product_uom_id.rounding
            # 校验本工序报工和报废的总数量是否等于上工序的良品数量
            # if float_compare(rec.qty_op_produced + rec.qty_op_reject, previous_qty, precision_rounding=rounding) == 1 or \
            #     float_compare(rec.qty_op_produced_pnl + rec.qty_op_reject_pnl, previous_pnl, precision_rounding=rounding) == 1:
            #     raise ValidationError(_('The total number of scrapped work reports is greater than the available quantity'))

    def consumable_materials_bcp(self):
        """
        完成半成品消耗
        """
        self.ensure_one()
        production = self.production_id

        moves = production.move_raw_ids.filtered(lambda m: m.product_id.categ_id.code and m.product_id.categ_id.code == 'BCP' and m.state not in ('done', 'cancel'))

        if not moves:
            return

        moves.update({'picked': True})
        for move in moves:
            product = move.product_id
            rounding = product.uom_id.rounding
            if move.product_id.tracking == 'none':
                move.quantity = move.product_qty
            else:
                move.move_line_ids.filtered(lambda x: not x.lot_id).unlink()
                move_qty = move.product_uom._compute_quantity(move.product_qty, product.uom_id)
                location_ids = self.env['stock.location'].search([('barcode', '=', 'WH-PREPRODUCTION')], limit=1)
                location_ids |= location_ids.child_ids
                children_production_ids = production._get_children().filtered(lambda x: x.product_id == product and x.state != 'cancel')
                children_qty = sum(children_production_ids.mapped('product_qty'))
                done_children_production_ids = production._get_children().filtered(lambda x: x.product_id == product and x.state == 'done')
                # not_done_children_production_ids = production._get_children().filtered(lambda x: x.product_id == product and x.state not in ('done', 'cancel'))

                # 子生产单数量不小于 消耗数量
                if float_compare(move_qty, children_qty, precision_rounding=rounding) != 1:
                    done_lot_ids = done_children_production_ids.mapped('lot_producing_id')
                    move.move_line_ids.filtered(lambda x: x.lot_id not in done_lot_ids and x.state != 'done').unlink()
                    domain = [
                        ('product_id', '=', move.product_id.id),
                        ('location_id', 'in', location_ids.ids),
                        ('lot_id', 'in', done_lot_ids.ids)
                    ]
                    quants = self.env['stock.quant'].search(domain)
                    quants_total = sum(quants.mapped('quantity'))
                    if float_compare(move_qty, quants_total, precision_rounding=rounding) != 1:
                        self._handle_lot_assignment(move, quants, rounding)
                    else:
                        raise ValidationError(_("%s Insufficient material inventory!", move.product_id.name))

                else:
                    if move.move_orig_ids:
                        orig_lot_ids = move.move_orig_ids.mapped('lot_ids').ids
                        domain = [
                            ('product_id', '=', move.product_id.id),
                            ('location_id', 'in', location_ids.ids),
                            ('lot_id', 'in', orig_lot_ids)
                        ]
                        quants = self.env['stock.quant'].search(domain)
                        quants_total = sum(quants.mapped('quantity'))
                        if float_compare(move_qty, quants_total, precision_rounding=rounding) != 1:
                            self._handle_lot_assignment(move, quants, rounding)
                        else:
                            production_childs = self.env['mrp.production'].search([
                                ('state', '=', 'done'),
                                ('base_mo', '=', production.base_mo),
                                ('product_id', '=', product.id)
                            ])
                            lot_ids = production_childs.move_finished_ids.mapped('move_line_ids.lot_id')
                            domain = [
                                ('product_id', '=', move.product_id.id),
                                ('location_id', 'in', location_ids.ids),
                                ('lot_id', 'in', lot_ids.ids)
                            ]
                            quants = self.env['stock.quant'].search(domain)
                            quants_total = sum(quants.mapped('quantity'))
                            if float_compare(move_qty, quants_total, precision_rounding=rounding) != 1:
                                self._handle_lot_assignment(move, quants, rounding)
                            else:
                                raise ValidationError(_("%s Insufficient material inventory!", move.product_id.name))
                    else:
                        domain = [
                            ('product_id', '=', move.product_id.id),
                            ('location_id', 'in', location_ids.ids),
                            ('lot_id', '!=', False)
                        ]
                        quants = self.env['stock.quant'].search(domain)
                        quants_total = sum(quants.mapped('quantity'))
                        if float_compare(move_qty, quants_total, precision_rounding=rounding) != 1:
                            self._handle_lot_assignment(move, quants, rounding)
                        else:
                            raise ValidationError(_("%s Insufficient material inventory!", move.product_id.name))

            move.workorder_id = self.id
            move._action_done()

    def consumable_materials(self):
        """
        特定工序完成物料消耗
        """
        self.ensure_one()
        production = self.production_id
        workcenter = self.workcenter_id

        # 按分类过滤移动项
        category_moves = defaultdict(list)
        for category in workcenter.s_material_category_ids:
            moves = production.move_raw_ids.filtered(
                lambda m: m.product_id.categ_id == category or category.is_parent_category(m.product_id.categ_id)
            )
            category_moves[category].extend(moves.filtered(lambda m: m.state not in ('done', 'cancel')))

        if not category_moves:
            return

        product_lots = defaultdict(dict)
        for moves in category_moves.values():
            for move in moves:
                move.update({'picked': True})
                location_id = workcenter.s_location_id or move.location_id
                if move.product_id.tracking == 'none':
                    continue  # 非批次追踪产品跳过
                # 根据move_orig_ids决定库存范围
                domain = [
                    ('product_id', '=', move.product_id.id),
                    ('location_id', '=', location_id.id),
                ]
                if move.move_orig_ids:
                    orig_lot_ids = move.move_orig_ids.mapped('lot_ids').ids
                    domain.append(('lot_id', 'in', orig_lot_ids))
                else:
                    domain.append(('lot_id', '!=', False))  # 确保有批次号

                quants = self.env['stock.quant'].search(domain)
                product_lots[move.product_id.id] = quants

        # 处理每个分类下的移动
        for category, moves in category_moves.items():
            for move in moves:
                product = move.product_id
                rounding = product.uom_id.rounding
                move_qty = move.product_uom._compute_quantity(move.product_uom_qty, product.uom_id)

                # 检查库存是否充足
                if product.tracking == 'none':
                    available_qty = sum(q.quantity for q in product_lots.get(product.id, []))
                    if float_compare(available_qty, move_qty, precision_rounding=rounding) < 0:
                        raise ValidationError(_("Insufficient inventory for %s!" % product.name))
                else:
                    # 清理非法批次的move_line
                    valid_lot_ids = product_lots[product.id].mapped('lot_id.id')
                    move.move_line_ids.filtered(
                        lambda l: l.lot_id.id not in valid_lot_ids and l.state != 'done'
                    ).unlink()

                    self._handle_lot_assignment(move, product_lots.get(product.id, []), rounding)
                move.workorder_id = self.id
                move._action_done()

    def _handle_lot_assignment(self, move, quants, rounding):
        """处理批次分配的辅助方法（强制使用原单批次）"""
        move_lines = move.move_line_ids.filtered(lambda l: l.state not in ('done', 'cancel'))
        move_lines.unlink()  # 清除未完成的批次分配

        remaining_qty = float_round(move.product_uom_qty, precision_rounding=rounding)
        for quant in quants.sorted(lambda q: (q.lot_id.name, q.id)):  # 固定排序保证一致性
            if float_is_zero(remaining_qty, precision_rounding=rounding):
                break

            available = quant.quantity
            if float_is_zero(available, precision_rounding=rounding):
                continue

            assign_qty = min(available, remaining_qty)
            self.env['stock.move.line'].create({
                'move_id': move.id,
                'product_id': move.product_id.id,
                'product_uom_id': move.product_uom.id,
                'location_id': quant.location_id.id,
                'location_dest_id': move.location_dest_id.id,
                'lot_id': quant.lot_id.id,
                'quantity': assign_qty,
                'date': datetime.now(),
                'company_id': move.company_id.id,
                'production_id': move.raw_material_production_id.id,
                'reference': move.raw_material_production_id.name or '',
                'lot_name': quant.lot_id.name,
            })
            remaining_qty -= assign_qty

        if not float_is_zero(remaining_qty, precision_rounding=rounding):
            raise ValidationError(_("%s Insufficient material inventory!", move.product_id.name))

    def _get_move_vals(self, production, product, picking_type):
        return {
            'company_id': self.env.user.company_id.id,
            'date': datetime.now(),
            'location_id': picking_type.default_location_src_id.id,
            'location_dest_id': picking_type.default_location_dest_id.id,
            'product_id': product.id,
            'name': product.display_name,
            'procure_method': 'make_to_stock',
            'product_uom': production.product_uom_id.id,
            'product_uom_qty': self.qty_op_produced,
        }

    def _create_production_receipt_order(self):
        """
        成品入库
        """
        self.ensure_one()
        if self.state != 'done':
            return
        production_id = self.production_id
        if not production_id:
            return
        stock_location_id = self.env['stock.location'].sudo().search([('barcode', '=', 'WH-STOCK')], limit=1)
        if production_id.move_finished_ids.filtered(lambda x: x.location_dest_id == stock_location_id):
            return
        # A/B拼版用转换后的产品
        # if production_id.s_makeup_product_id:
        #     product_id = production_id.s_makeup_product_id
        #     lot_id = self.env['stock.lot'].search([('name', '=', production_id.lot_producing_id.name), ('product_id', '=', product_id.id)], limit=1)
        # else:
        product_id = production_id.product_id
        # lot_id = production_id.lot_producing_id

        picking_type = self.env['stock.picking.type'].search([('barcode', '=', 'INT-PRODUCTIONIN')], limit=1)
        origin = f'{product_id.name} / {production_id.base_mo or ""}'
        picking_id = self.env['stock.picking'].search([
            ('picking_type_id', '=', picking_type.id),
            ('origin', '=', origin),
            ('state', 'not in', ('done', 'cancel'))
        ], limit=1)

        if picking_id:
            move_ids = picking_id.move_ids.filtered(lambda x: x.product_id == product_id and x.state not in ('done', 'cancel'))
            if move_ids:
                move_id = move_ids[0]
                move_id.product_uom_qty += self.qty_op_produced
            else:
                move_id = self.env['stock.move'].sudo().create(self._get_move_vals(production_id, product_id, picking_type))
        else:
            location_dest_id = picking_type.default_location_dest_id
            if product_id.categ_id.code and product_id.categ_id.code == 'OPFP':
                location_dest_id = self.env['stock.location'].search([('barcode', '=', 'STOCK-OP')], limit=1)
            vals = {
                'company_id': self.env.user.company_id.id,
                'location_id': picking_type.default_location_src_id.id,
                'location_dest_id': location_dest_id.id,
                'move_type': 'direct',
                'picking_type_id': picking_type.id,
                'move_ids': [(0, 0, self._get_move_vals(production_id, product_id, picking_type))],
                'origin': origin
            }
            picking_id = self.env['stock.picking'].sudo().create(vals)
            move_id = picking_id.move_ids.filtered(lambda x: x.product_id == product_id)

        # move_line_ids_vals_list = []
        # for line in production_id.finished_move_line_ids.sorted(lambda x: x.id):
        #     move_line_ids_vals_list.append()
        move_id.write({
            'move_orig_ids': [(4, m.id) for m in production_id.move_finished_ids],
            # 'move_line_ids': [(0, 0, {
            #     'product_id': product_id.id,
            #     'product_uom_id': production_id.product_uom_id.id,
            #     'location_id': move_id.location_id.id,
            #     'location_dest_id': move_id.location_dest_id.id,
            #     'lot_id': lot_id.id,
            #     'quantity': self.qty_op_produced,
            #     'date': datetime.now(),
            #     'company_id': move_id.company_id.id,
            #     'production_id': production_id.id,
            #     'reference': production_id.name or '',
            #     'lot_name': production_id.lot_producing_id.name,
            # })]
        })
        move_id._action_assign()
        move_id.move_line_ids._apply_putaway_strategy()
        if picking_id.state == 'draft':
            picking_id.action_confirm()
            picking_id.action_assign()

    def _update_batch(self):
        for rec in self:
            if rec.state == 'done':
                # 完成状态设置批次完成时间,包装工序PKG01
                if not rec.workcenter_id.code or rec.workcenter_id.code != 'PKG01':
                    continue
                production = rec.production_id
                if not production.lot_producing_id:
                    continue
                # production.lot_producing_id.s_week_of_year = production.date_code
                if not production.lot_producing_id.production_date:
                    production.lot_producing_id.production_date = rec.date_finished or datetime.now()

                # 包装工序完成时，创建生产入库单
                # rec._create_production_receipt_order()

    @api.depends('state', 'done_line_ids', 's_scrap_ids')
    def compute_qty_floor(self):
        for record in self:
            qty_wip = 0
            qty_wip_pnl = 0
            qty_wip_set = 0

            qty_op_reject = (
                    sum(record.done_line_ids.mapped('qty_reject')) +
                    sum(record.s_scrap_ids.mapped('scrap_qty'))
            )
            qty_op_reject_set = (
                    sum(record.done_line_ids.mapped('qty_reject_set')) +
                    sum(record.s_scrap_ids.mapped('scrap_set_qty'))
            )
            qty_op_reject_pnl = (
                    sum(record.done_line_ids.mapped('qty_reject_pnl')) +
                    sum(record.s_scrap_ids.mapped('scrap_panel_qty'))
            )

            qty_op_produced = sum(record.done_line_ids.mapped('qty_produced'))
            qty_op_produced_set = sum(record.done_line_ids.mapped('qty_produced_set'))
            qty_op_produced_pnl = sum(record.done_line_ids.mapped('qty_produced_pnl'))

            uom_values = {x.uom_id.code: x.uom_value for x in record.production_id.uom_value_ids}
            value_pnl = uom_values.get('PANEL', 1)
            value_set = uom_values.get('SET', 1)

            if record.state not in ('cancel', 'pending', 'done'):
                if record._check_first_work():
                    if record.production_id._get_children().filtered(lambda p: p.state not in ('done', 'cancel')):
                        qty_wip = 0
                        qty_wip_pnl = 0
                        qty_wip_set = 0
                    else:
                        qty_wip = record.qty_production - (qty_op_produced + qty_op_reject)
                        qty_wip_set = (record.qty_production / value_set) - (qty_op_produced_set + qty_op_reject_set)
                        qty_wip_pnl = (record.qty_production / value_pnl) - (qty_op_produced_pnl + qty_op_reject_pnl)
                else:
                    prev_wo = record.env['mrp.workorder'].search([
                        ('production_id', '=', record.production_id.id),
                        ('step', '<', record.step)
                    ], order="step desc", limit=1)
                    if prev_wo:
                        qty_wip = prev_wo.qty_op_produced - qty_op_produced - qty_op_reject
                        qty_wip_pnl = prev_wo.qty_op_produced_pnl - qty_op_produced_pnl - qty_op_reject_pnl
                        qty_wip_set = prev_wo.qty_op_produced_set - qty_op_produced_set - qty_op_reject_set

            if qty_wip == 0:
                qty_wip_pnl, qty_wip_set = 0, 0
            record.update({
                'qty_wip': max([qty_wip, 0]),
                'qty_wip_pnl': max([qty_wip_pnl, 0]),
                'qty_wip_set': max([qty_wip_set, 0]),
                'qty_op_produced': qty_op_produced,
                'qty_op_produced_set': qty_op_produced_set,
                'qty_op_produced_pnl': qty_op_produced_pnl,
                'qty_op_reject': qty_op_reject,
                'qty_op_reject_set': qty_op_reject_set,
                'qty_op_reject_pnl': qty_op_reject_pnl
            })

    @api.depends('operation_id')
    def _compute_prev_step_done(self):
        for record in self:
            record.prev_step_done = False
            curr_step = record.operation_id.step
            if curr_step == 1:
                record.prev_step_done = True
            else:
                workorder_ids = record.env['mrp.workorder'].search([
                    ('production_id', '=', record.production_id.id),
                    ('step', '<', record.step),
                    ('state', '!=', 'done'),
                ], limit=1)
                if workorder_ids:
                    record.prev_step_done = False
                else:
                    record.prev_step_done = True

    @api.depends('curr_user', 'workcenter_id')
    def _compute_is_allow_move(self):
        for record in self:
            record.is_allow_move = False
            if record.workcenter_id.employee_ids:
                if record.env.user.employee_id in record.workcenter_id.employee_ids:
                    record.is_allow_move = True
            else:
                record.is_allow_move = True

    def _check_jump_production_report_to_work(self):
        """
        检查工序报工是否有跳卡
        例如：一批有 1，2，3号工卡，同工序报工必须按工卡顺序报工
        """
        self.ensure_one()
        production_id = self.production_id
        if not production_id:
            return
        previous_production = self.env['mrp.production'].search([
            ('base_mo', '=', production_id.base_mo),
            ('level_no', '=', production_id.level_no),
            ('seq_no', '<', production_id.seq_no),
        ], limit=1, order='seq_no desc')
        if not previous_production:
            return

        same_work_order = previous_production.workorder_ids.filtered(lambda x: x.workcenter_id == self.workcenter_id)
        if not same_work_order:
            return

        if same_work_order.filtered(lambda x: x.state != 'done'):
            raise UserError(_('It is not allowed to skip production order operations, '
                              'Production (%s) , WorkOrder (%s) Not completed',
                              previous_production.name, same_work_order[0].name))

    def button_start(self):
        if self.mapped('production_id').filtered(lambda x: x.s_order_suspension):
            raise UserError(_('The order has been paused and no work is allowed to start!'))

        for rec in self:
            if all([self.env.user not in rec.work_station_id.user_ids, not self.env.user.has_group('pcbmanufact.group_s_mrp_plan'), not self.env.su]):
                raise UserError(_('Ussr (%s), Work Station (%s), You are not authorized to process this station work order', self.env.user.name, rec.work_station_id.name))
            # 判断同一批生产单报工是否有跳生产单报工
            if not self.env['ir.config_parameter'].sudo().get_param('pcbmanufact.s_production_jump_work'):
                rec._check_jump_production_report_to_work()

            # 子工单有报废，本生产单需要在设置的工序登记总的报废数量
            if rec.workcenter_id.s_scrap_point and \
                    rec.production_id._get_children() and \
                    sum(rec.production_id._get_children().mapped('workorder_ids.qty_op_reject')) > 0 and \
                    not rec.production_id.s_registered:
                return {
                    'type': 'ir.actions.act_window',
                    'name': _('Registration Good Qty'),
                    'view_mode': 'form',
                    'res_model': 's.registration.good.qty.wizard',
                    'target': 'new',
                    'context': {
                        'default_production_id': rec.production_id.id,
                        'default_workorder_id': rec.id
                    }
                }
            # 判断是否为首工序
            if rec._check_first_work():
                # 判断子工单是否有报废单未完成
                # if rec.production_id._get_children().mapped('scrap_ids').filtered(lambda x: x.state != 'done'):
                #     raise UserError(_(
                #         'The inner production order is associated with an unfinished scrap order. '
                #         'Please notify the relevant personnel to handle it before starting the operation'
                #     ))

                if rec.production_id._get_children().filtered(lambda x: x.state not in ('done', 'cancel')):
                    raise UserError(_('The sub work order has not been completed, and it is not allowed to start the job'))

            if not rec.prev_step_done:
                message = _("The current process %s cannot be processed. Please first complete step %s.",
                            rec.workcenter_id.name.strip(), rec.operation_id.step - 1)
                raise ValidationError(message)
            if not rec.is_allow_move:
                message = _("User %s does not belong to this work center: %s.", self.env.user.login,
                            rec.workcenter_id.name.strip())
                raise ValidationError(message)
            if rec.state not in ['ready', 'progress'] and rec.qty_wip == 0:
                message = _("The current process %s cannot be processed. The status is: %s.",
                            rec.workcenter_id.name.strip(), rec.state)
                raise ValidationError(message)
            if rec.qty_wip == 0:
                message = _("The current process %s cannot be processed. The backlog number is: 0.",
                            rec.workcenter_id.name.strip())
                raise ValidationError(message)

        return super().button_start()

    def button_restart_start(self):
        next_wo = self.env['mrp.workorder'].search([
            ('production_id', '=', self.production_id.id),
            ('step', '=', self.step + 1)
        ])
        if next_wo:
            if next_wo.state == 'done' or next_wo.done_act_qty > 0:
                message = _("The next process %s has been completed or scrapped. Please revoke it first.",
                            next_wo.workcenter_id.name.strip())
                raise ValidationError(message)
        vals = {
            'done_act_qty': 0,
            'state': 'pending',
        }
        lines = self.env['mrp.workorder.line'].search([('workorder_id', '=', self.id)])
        totrej = 0
        for l in lines:
            totrej += l.qty_reject
            l.unlink()

        if totrej > 0:
            scrap_lines = self.env['stock.scrap'].search([('workorder_id', '=', self.id)])
            for l in scrap_lines:
                l.unlink()

        qty_reject = self.production_id.qty_reject - totrej

        pvals = {
            'qty_reject': qty_reject
        }
        self.production_id.write(pvals)
        self.write(vals)

    def button_finish(self):
        if self.mapped('production_id').filtered(lambda x: x.s_order_suspension):
            raise UserError('The order has been paused and no work is allowed to start!')
        for wo in self:
            if all([self.env.user not in wo.work_station_id.user_ids, not self.env.user.has_group('pcbmanufact.group_s_mrp_plan'), not self.env.su]):
                raise UserError(_('Ussr (%s), Work Station (%s), You are not authorized to process this station work order', self.env.user.name, wo.work_station_id.name))
            if wo.state in ('done', 'cancel'):
                wo.verify_quality_checks()
                return super().button_finish()

            check_ids = []
            for check in wo.check_ids:
                if check.quality_state in ['pass', 'fail']:
                    continue
                if check.test_type in ['register_consumed_materials', 'register_byproducts', 'instructions']:
                    check.quality_state = 'pass'
                else:
                    check_ids.append(check.id)
            if len(check_ids) > 0:
                action = (
                    self.env.ref("quality_control.quality_check_action_main")
                    .sudo()
                    .with_context(active_id=check_ids[0])
                    .read()[0]
                )
                if len(check_ids) > 1:
                    action["domain"] = [("id", "in", check_ids)]
                else:
                    action.update(
                        res_id=check_ids[0],
                        view_mode="form",
                        view_id=False,
                        views=False,
                    )
                return action
            wip = wo.qty_wip
            if wip == 0:
                message = _("The current process %s cannot be processed, the backlog number is: 0.", wo.workcenter_id.name.strip())
                raise ValidationError(message)

            # 检查是否是外包加工, pol_ids是一个扩展字段，在MO拆分的时候使用
            if wo.is_subcontracting:
                pol_ids = wo.pol_id | wo.pol_ids
                if not pol_ids.filtered(lambda r: r.state != 'cancel'):
                    raise ValidationError(_('Please create a purchase order to proceed with subcontracting operations.'))
                # if not rec.sb_pol_id.filtered(lambda r: r.state != 'cancel').mapped(''):
                #     raise ValidationError(_('请下达采购订单进行外包加工操作'))
                # 检查是否完成出库入库操作
                for i in pol_ids.filtered(lambda r: r.state != 'cancel'):
                    if not i.order_id.subcon_in:
                        raise ValidationError(_('Subcontracting: Order = {}, please complete the delivery operation.').format(i.order_id.name))
                    if not i.order_id.subcon_out:
                        raise ValidationError(_('Subcontracting: Order = {}, please complete the receipt operation.').format(i.order_id.name))
                # 检查外包加工数量是否足够

            new_wizard = self.env['workorder.done.line.qty'].create({'workorder_id': wo.id})
            view_id = self.env.ref('pcbmanufact.view_workorder_wizard').id
            if self._context.get('auto_finish', None):
                return new_wizard

            return {
                'type': 'ir.actions.act_window',
                'name': _('Work Order Production Counting'),
                'view_mode': 'form',
                'res_model': 'workorder.done.line.qty',
                'target': 'new',
                'res_id': new_wizard.id,
                'views': [[view_id, 'form']],
            }

    def _get_panel_width_height(self, product_id):
        uom_values = {x.uom_id.name: x.uom_value for x in product_id.uom_value_ids}
        panel_height = uom_values.get('PNLL', 0)
        panel_width = uom_values.get('PNLW', 0)
        panel_qty = uom_values.get('PANEL', 0)
        return panel_height, panel_width, panel_qty

    def action_workorder_subcontracting(self):
        """
        工序委外，向导
        """
        logging.info("action_workorder_subcontracting")
        workorder = self.filtered(lambda r: r.is_subcontracting == True)
        view = self.env.ref("pcbmanufact.workorder_subcontracting_wizard_form")
        context = dict(self.env.context, )
        line_list = []
        for i in workorder:
            panel_height, panel_width, panel_qty = self._get_panel_width_height(i.product_id)
            done_qty = sum(i.pol_id.filtered(lambda r: r.state != 'cancel').mapped('product_qty'))
            # qty = i.qty_remaining - done_qty
            qty = i.qty_remaining - done_qty
            area = 0
            if panel_qty:
                area = qty / panel_qty * panel_height * panel_width / 1000000
            if qty <= 0:
                continue
            line_list.append((0, 0, {
                'workorder_id': i.id,
                'done_qty': done_qty,
                'qty': qty,
                'area': area
            }))
        context.update({
            'default_line_ids': line_list,
        })
        return {
            "name": _("Process Outsourcing Wizard"),
            "type": "ir.actions.act_window",
            "view_mode": "form",
            "res_model": "workorder.subcontracting.wizard",
            "views": [(view.id, "form")],
            "view_id": view.id,
            "target": "new",
            "context": context,
        }

    def chargeback(self):
        """
        退回到上工序，
        """
        self.ensure_one()
        if self.state not in ('ready', 'progress'):
            return
        now_work_str = self.name
        work_str = ''
        self.done_line_ids.unlink()
        self.write({'qty_produced': 0, 'state': 'pending', 'done_act_qty': 0, 'allow_producing_quantity_change': False})
        previous_work_ids = self.production_id.workorder_ids.filtered(lambda x: x.step < self.step).sorted('step', reverse=True)
        for work in previous_work_ids:
            work.done_line_ids.unlink()
            work.write({'qty_produced': 0, 'state': 'pending'})
            if any([work.workcenter_id.is_enter_bcode, work.s_scrap_ids, work.is_subcontracting]):
                work.state = 'ready'
                work_str = work.name
                break
        msg = _('refund %s - %s', now_work_str, work_str)
        self.production_id.message_post(body=msg)

    @api.model
    def _name_search(self, name, domain=None, operator='ilike', limit=None, order=None):

        if self._context.get('search_type', '') == 'scrap':
            workorder_id = self._context.get('workorder_id', None)
            if workorder_id:
                workorder_id = self.env['mrp.workorder'].browse(workorder_id)
                workorder_ids = self.env['mrp.workorder'].search([
                    ('production_id', '=', workorder_id.production_id.id),
                    ('step', '<', workorder_id.step)
                ], order="step desc")

                return self._search(domain=[('id', 'in', workorder_ids.ids)], limit=limit, order=order)

        return super()._name_search(name, domain, operator, limit, order)

    def _set_dates(self):
        """原生方法在计算时无法计算出完成时间，修改成当前时间"""
        for wo in self.sudo():
            if wo.leave_id:
                if not wo.date_start:
                    wo.date_start = datetime.now()
                if not wo.date_finished:
                    wo.date_finished = datetime.now()
                wo.leave_id.write({
                    'date_from': wo.date_start,
                    'date_to': wo.date_finished,
                })
            elif wo.date_start and wo.date_finished:
                wo.leave_id = wo.env['resource.calendar.leaves'].create({
                    'name': wo.display_name,
                    'calendar_id': wo.workcenter_id.resource_calendar_id.id,
                    'date_from': wo.date_start,
                    'date_to': wo.date_finished,
                    'resource_id': wo.workcenter_id.resource_id.id,
                    'time_type': 'other',
                })

    def _compute_state(self):
        for rec in self:
            previous = rec._get_previous_work()
            if rec.state == 'pending':
                if not previous:
                    if rec.production_id._get_children().filtered(lambda p: p.state not in ('done', 'cancel')):
                        rec.state = 'waiting'
                    else:
                        rec.state = 'ready'
                else:
                    if previous.state == 'done':
                        rec.state = 'ready'
                    else:
                        rec.state = 'pending'
            if rec.state not in ('waiting', 'ready'):
                continue

            if rec.state == 'waiting':
                if not rec.production_id._get_children().filtered(lambda p: p.state not in ('done', 'cancel')):
                    rec.state = 'ready'
            if rec.state == 'ready':
                if previous and previous.state != 'done':
                    rec.state = 'pending'

    def _check_no_cyclic_dependencies(self):
        """去掉周期依赖性的检验"""
        ...


class MrpWorkorderLine(models.Model):
    _name = 'mrp.workorder.line'
    _description = 'Work Order Line'  # 工单过数明细

    workorder_id = fields.Many2one('mrp.workorder', "Work Order")
    user_id = fields.Many2one('res.users', string="Counting User",
                              ondelete='restrict', required=True, index=True, default=lambda self: self.env.user)
    qty_produced = fields.Float(string='Produced')
    qty_reject = fields.Float(string='Scrap', default=0)
    qty_produced_pnl = fields.Float(string='Produced (PNL)')
    qty_reject_pnl = fields.Float(string='Scrap (PNL)', default=0)
    qty_produced_set = fields.Float(string='Produced (SET)')
    qty_reject_set = fields.Float(string='Scrap (SET)', default=0)
    active = fields.Boolean(default=True)

